package de.fhwgt.quiz.servlets;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.util.ArrayList;
import java.util.TimerTask;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;

import org.apache.catalina.websocket.MessageInbound;
import org.apache.catalina.websocket.StreamInbound;
import org.apache.catalina.websocket.WebSocketServlet;
import org.apache.catalina.websocket.WsOutbound;
import java.io.*; 
import javax.xml.parsers.*; 
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.*; 
import org.xml.sax.*; 

import de.fhwgt.quiz.application.Player;
import de.fhwgt.quiz.application.Question;
import de.fhwgt.quiz.application.Quiz;
import de.fhwgt.quiz.error.QuizError;
/**
 * Servlet implementation class WebSocketQuizServlet
 */
public class WebSocketQuizServlet extends WebSocketServlet {
    private static final long serialVersionUID = 1L;
    private Quiz quiz;
    private static ArrayList<MyMessageInbound> playerList = new ArrayList<MyMessageInbound>();
   
 
    public WebSocketQuizServlet(){
    	quiz = Quiz.getInstance();
    
			
		new Thread(){
			public void run(){
				while(true){
					quiz.waitPlayerChange();
					//System.out.println("ARRAY;"+quiz.getPlayerList().size());
					if(quiz.getPlayerList().size()==0){		//Superuser hat das Spiel verlassen, alle websockets schließen
		                for(MyMessageInbound mmib: playerList){
		                	try {
			                	mmib.myoutbound.close(0,null);
								//mmib.myoutbound.flush();		
							} catch (IOException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
		                }
		                playerList.removeAll(playerList);
					}
					if(quiz.getPlayerList().size()>1){
						if(quiz.getCurrentCatalog()!=null){
							try {
								sendGameReady();
							} catch (Exception e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
						}
					}
					if(quiz.getPlayerList().size()==1){
						try {
							//System.out.println("Game is not ready!");
			    			DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
			    			DocumentBuilder docBuilder;
							docBuilder = docFactory.newDocumentBuilder();
			    			Document doc = docBuilder.newDocument();
			    			Element rootElement = doc.createElement("gamenotready");
			    			doc.appendChild(rootElement);
			    			String output = DOMtoString(doc);
							for(MyMessageInbound mmib: playerList){//Spiel kann in nächste Phase gehen
			                    CharBuffer buffer = CharBuffer.wrap(output.toCharArray());
			                    mmib.myoutbound.writeTextMessage(buffer);
			                    mmib.myoutbound.flush();
							}
						} catch (Exception e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
		    			// root elements
					}
					//System.out.println("SIZEe: "+playerList.size());
				}
			}
		}.start();

    	


    }

	@Override
    protected StreamInbound createWebSocketInbound(String string, HttpServletRequest hsr) {
        return new MyMessageInbound();
    }
    private class MyMessageInbound extends MessageInbound{
        WsOutbound myoutbound;
 
        @Override
        public void onOpen(WsOutbound outbound){
            //System.out.println("Open Client.");
			this.myoutbound = outbound;
			playerList.add(this);
        }
 
        @Override
        public void onClose(int status){
            //System.out.println("Close Client.");
            playerList.remove(this);
        }
 
        @Override
        public void onTextMessage(CharBuffer cb) throws IOException{
        	
        	try {
        		System.out.println("XML from client::"+cb.toString());
				Document xml = stringToDom(cb.toString());
				doSomething(xml, myoutbound);
				
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
            //System.out.println(cb.toString());
            //WsOutbound outbound = getWsOutbound();
            //outbound.writeTextMessage(cb);
        }
 
        @Override
        public void onBinaryMessage(ByteBuffer bb) throws IOException{
        }
    }
 
    

    public void doSomething(Document xml, final WsOutbound myoutbound) throws IOException, ParserConfigurationException, TransformerException{
    	QuizError error = new QuizError();
    	String playerid;
    	String playername;
    	int caseFall = 99;
    	if(xml.getFirstChild().getNodeName().equals("changecatalog"))
    		caseFall = 0;
    	if(xml.getFirstChild().getNodeName().equals("startgame"))
    		caseFall = 1;
    	if(xml.getFirstChild().getNodeName().equals("requestquestion"))
    		caseFall = 2;
    	if(xml.getFirstChild().getNodeName().equals("questionresult"))
    		caseFall = 3;
    		
    	switch(caseFall){

    	case 0:
    		playername = xml.getElementsByTagName("playername").item(0).getTextContent();
    		playerid = xml.getElementsByTagName("playerid").item(0).getTextContent();
    		String catalog = xml.getElementsByTagName("catalog").item(0).getTextContent();
    		//System.out.println("::changecatalog:: name: "+playername+"  ID: "+playerid+"  catalog: "+catalog);
    		
    		for(Player p : quiz.getPlayerList())
    			if(p.getName().equals(playername)&&p.getId().equals(Long.valueOf(playerid))){
    				quiz.changeCatalog(p, catalog, error);//während der Spielphase kann der Katalog nicht mehr geändert werden.
    				if(!error.isSet()&&quiz.getPlayerList().size()>1){
    					sendGameReady();
    				}
    			}
    		if(!error.isSet()){
    			DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
    			DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
    			// root elements
    			Document doc = docBuilder.newDocument();
    			Element rootElement = doc.createElement("refreshcatalog");
    			doc.appendChild(rootElement);
    			String output = DOMtoString(doc);					
    			//System.out.println(output);


                for(MyMessageInbound mmib: playerList){
                    CharBuffer buffer = CharBuffer.wrap(output.toCharArray());
                    mmib.myoutbound.writeTextMessage(buffer);
                    mmib.myoutbound.flush();
                }
    		}
    			
    			
    		break;	
    	case 1:
    		
    		//System.out.println("starting game ...");
    		playername = xml.getElementsByTagName("playername").item(0).getTextContent();
    		playerid = xml.getElementsByTagName("playerid").item(0).getTextContent();
    		//System.out.println("::startgame:: name: "+playername+"  ID: "+playerid);
    		for(Player p : quiz.getPlayerList())
    			if(p.getName().equals(playername)&&p.getId().equals(Long.valueOf(playerid))){
    				quiz.startGame(p, error);
    				if(!error.isSet()){
    					sendgamestarted();
    				}
    			}
    		break;
    	case 2:
    		//System.out.println("Catalog was requested");
    		playername = xml.getElementsByTagName("playername").item(0).getTextContent();
    		playerid = xml.getElementsByTagName("playerid").item(0).getTextContent();
    		//System.out.println("::requestquestion:: name: "+playername+"  ID: "+playerid);
    		for(Player p : quiz.getPlayerList())
    			if(p.getName().equals(playername)&&p.getId().equals(Long.valueOf(playerid))){
    				Question q = quiz.requestQuestion(p, new TimerTask(){
    					@Override
    					public void run() {
    						try {
    						
								sendtimeout(myoutbound);
							} catch (Exception e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}  						
    					}}, error);
    				if(!error.isSet()){
    					if(q!=null){
    						responseQuestion(q, myoutbound);
    					}else{
    				    	if(quiz.setDone(p)){
        						try {
        							for(MyMessageInbound w :playerList){
        								sendGameOver(w.myoutbound);
        							}
        							for(Player player : quiz.getPlayerList()){
        								quiz.removePlayer(player, error);
        							}
    							} catch (Exception e) {
    								// TODO Auto-generated catch block
    								//System.out.println("Error on GameOver for "+p.getName());
    							}
    				    	}else{
    				    		try {
    				    			//System.out.println("Fragen alle .... ("+p.getName()+")");
    								sendGameOverWait(myoutbound);
    							} catch (Exception e) {
    								// TODO Auto-generated catch block
    								//System.out.println("Error on GameOver for "+p.getName());
    							}
    				    	}

    					}
    					
    				}else{
    					//System.out.println(error.getDescription());
    				}
    			}   		
    		break;
    	
    	case 3:
    		
    		String id = xml.getElementsByTagName("answerid").item(0).getTextContent();
    		playername = xml.getElementsByTagName("playername").item(0).getTextContent();
    		playerid = xml.getElementsByTagName("playerid").item(0).getTextContent();
    		//System.out.println("Frage wurde beantwortet: "+playername+" "+id);
    		
    		for(Player p : quiz.getPlayerList())
    			if(p.getName().equals(playername)&&p.getId().equals(Long.valueOf(playerid))){
    				
    				long result = quiz.answerQuestion(p, Integer.parseInt(id), error);
    				if(result==Integer.parseInt(id)){
    					sendAnswer(true, myoutbound, result, Integer.parseInt(id));
    				}else{
    					sendAnswer(false, myoutbound, result, Integer.parseInt(id));
    				}
    				// Frage richtig, berecne Punkte (und sende neuen Punktestand)
    				// oder Frage war falsch, -> keine punkte
    				
    				if(!error.isSet()){
    					
    				}else{
    					//System.out.println(error.getDescription());
    				}
    			} 
    		
    		break;
    		
    	default:	
    		
    		
    	}
    	
    }
    private void sendGameOverWait(WsOutbound myoutbound) throws Exception{
    	//System.out.println("Spiel Ende");
    	

    	Element rootElement;
		DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
		DocumentBuilder docBuilder;
		docBuilder = docFactory.newDocumentBuilder();
		Document doc = docBuilder.newDocument();

		rootElement = doc.createElement("gameoverwait");

		
		doc.appendChild(rootElement);
		String output = DOMtoString(doc);
		System.out.println("sendGameOverWait:: "+output);
        CharBuffer buffer = CharBuffer.wrap(output.toCharArray());
        myoutbound.writeTextMessage(buffer);
        myoutbound.flush();
		
		
		
	}

	private void sendGameOver(WsOutbound myoutbound) throws Exception {
    	//System.out.println("Spiel Ende");
    	

    	Element rootElement;
		DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
		DocumentBuilder docBuilder;
		docBuilder = docFactory.newDocumentBuilder();
		Document doc = docBuilder.newDocument();

		rootElement = doc.createElement("gameover");

		
		doc.appendChild(rootElement);
		String output = DOMtoString(doc);
		System.out.println("sendGameOver:: "+output);
        CharBuffer buffer = CharBuffer.wrap(output.toCharArray());
        myoutbound.writeTextMessage(buffer);
        myoutbound.flush();
		
		
	}

	private void sendAnswer(boolean b, WsOutbound myoutbound, long result, int i) throws ParserConfigurationException, TransformerException, IOException {
    	//System.out.println("Timeout");
    	Element rootElement;
		DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
		DocumentBuilder docBuilder;
		docBuilder = docFactory.newDocumentBuilder();
		Document doc = docBuilder.newDocument();
		if(b){
			rootElement = doc.createElement("anwerright");
		}else{
			rootElement = doc.createElement("anwerfalse");
		}
		
		Element geschickteID = doc.createElement("geschickteID");
		geschickteID.appendChild(doc.createTextNode(i+""));
		rootElement.appendChild(geschickteID);
		
		doc.appendChild(rootElement);
		String output = DOMtoString(doc);
		System.out.println("sendAnswer:: "+output);
        CharBuffer buffer = CharBuffer.wrap(output.toCharArray());
        myoutbound.writeTextMessage(buffer);
        myoutbound.flush();
		
	}

	protected void sendtimeout(WsOutbound myoutbound) throws ParserConfigurationException, TransformerException, IOException {
    	//System.out.println("Timeout");
		DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
		DocumentBuilder docBuilder;
		docBuilder = docFactory.newDocumentBuilder();
		Document doc = docBuilder.newDocument();
		Element rootElement = doc.createElement("questiontimeout");
		doc.appendChild(rootElement);
		String output = DOMtoString(doc);
		System.out.println("sendtimeout:: "+output);
        CharBuffer buffer = CharBuffer.wrap(output.toCharArray());
        myoutbound.writeTextMessage(buffer);
        myoutbound.flush();
		
		
    }
    private void responseQuestion(Question q, WsOutbound myoutbound) throws ParserConfigurationException, TransformerException, IOException {
    	//System.out.println("response question");
		DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
		DocumentBuilder docBuilder;
		docBuilder = docFactory.newDocumentBuilder();
		Document doc = docBuilder.newDocument();
		Element rootElement = doc.createElement("responsequestion");
		Element question = doc.createElement("question");
		question.appendChild(doc.createTextNode(q.getQuestion()));
		rootElement.appendChild(question);
		//q.shuffleAnswers();
		Element anwers = doc.createElement("answers");
		
		for(int i =0;i< q.getAnswerList().size();i++) {		
			Element answerEntry = doc.createElement("answer");
			answerEntry.appendChild(doc.createTextNode(q.getAnswerList().get(i)));
			answerEntry.setAttribute("index", ""+i);
			anwers.appendChild(answerEntry);			
		}
		rootElement.appendChild(anwers);
		Element correct = doc.createElement("correct");
		correct.appendChild(doc.createTextNode(q.getCorrectIndex()+""));
		rootElement.appendChild(correct);
		Element timeout = doc.createElement("timeout");
		timeout.appendChild(doc.createTextNode(q.getTimeout()+""));
		rootElement.appendChild(timeout);
		doc.appendChild(rootElement);
		String output = DOMtoString(doc);
		System.out.println("responseQuestion:: "+output);
        CharBuffer buffer = CharBuffer.wrap(output.toCharArray());
        myoutbound.writeTextMessage(buffer);
        myoutbound.flush();
		
	}
	private void sendgamestarted() throws ParserConfigurationException, TransformerException, IOException {
		//System.out.println("Game is started!");
		DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
		DocumentBuilder docBuilder;
		docBuilder = docFactory.newDocumentBuilder();
		Document doc = docBuilder.newDocument();
		Element rootElement = doc.createElement("gamestarted");
		doc.appendChild(rootElement);
		String output = DOMtoString(doc);
		System.out.println("sendgamestarted:: "+output);
		for(MyMessageInbound mmib: playerList){//Spiel kann in nächste Phase gehen
            CharBuffer buffer = CharBuffer.wrap(output.toCharArray());
            mmib.myoutbound.writeTextMessage(buffer);
            mmib.myoutbound.flush();
		}
		
	}
	private void sendGameReady() throws IOException, TransformerException, ParserConfigurationException {
		//System.out.println("Game is ready!");
		DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
		DocumentBuilder docBuilder;
		docBuilder = docFactory.newDocumentBuilder();
		Document doc = docBuilder.newDocument();
		Element rootElement = doc.createElement("gameready");
		doc.appendChild(rootElement);
		String output = DOMtoString(doc);
		System.out.println("sendGameReady:: "+output);
		for(MyMessageInbound mmib: playerList){//Spiel kann in nächste Phase gehen
            CharBuffer buffer = CharBuffer.wrap(output.toCharArray());
            mmib.myoutbound.writeTextMessage(buffer);
            mmib.myoutbound.flush();
		}

	}
	public String DOMtoString(Document doc) throws TransformerException{
    	TransformerFactory tf = TransformerFactory.newInstance();
		Transformer transformer = tf.newTransformer();
		transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
		StringWriter writer = new StringWriter();
		transformer.transform(new DOMSource(doc), new StreamResult(writer));
		return writer.getBuffer().toString().replaceAll("\n|\r", "");
    }
    public Document stringToDom(String xmlSource) 
		throws SAXException, ParserConfigurationException, IOException { 
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 
		DocumentBuilder builder = factory.newDocumentBuilder(); 
		return builder.parse(new InputSource(new StringReader(xmlSource))); 
    }
    
}